/**
 * Copyright (c) 2021 OceanBase
 * OceanBase CE is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#ifndef OCEANBASE_SQL_OB_FD_ITEM_H
#define OCEANBASE_SQL_OB_FD_ITEM_H
#include "lib/allocator/page_arena.h"
#include "sql/resolver/dml/ob_raw_expr_sets.h"
namespace oceanbase {
namespace sql {
enum FdLevel { INVALID_LEVEL = -1, TABLE_LEVEL, EXPR_LEVEL };

/**
 * Fd means function dependent, for table t1 (c1 int primary key, c2 int, c3 int),
 * we known c1 can decide c2 and c3, then we say c1 function dependent c1, c2, c3,
 * or c1 function dependent table t1
 * here [c1]          is parent exprs
 *      [c1, c2, c3]  is child exprs
 *      {t1}          is child tables
 */
class ObFdItem {
public:
  ObFdItem(const bool is_unique = false, ObRawExprSet* parent_exprs = NULL, const int32_t stmt_level = -1)
      : parent_exprs_(parent_exprs), is_unique_(is_unique), stmt_level_(stmt_level)
  {}
  virtual ~ObFdItem()
  {}

  inline ObRawExprSet* get_parent_exprs() const
  {
    return parent_exprs_;
  }
  inline void set_parent_exprs(ObRawExprSet* parent_exprs)
  {
    parent_exprs_ = parent_exprs;
  }
  inline bool is_unique() const
  {
    return is_unique_;
  }
  inline void set_is_unique(const bool is_unique)
  {
    is_unique_ = is_unique;
  }
  inline int32_t get_stmt_level() const
  {
    return stmt_level_;
  }
  inline void set_stmt_level(const int32_t stmt_level)
  {
    stmt_level_ = stmt_level;
  }
  virtual inline FdLevel get_level() const
  {
    return INVALID_LEVEL;
  }
  virtual inline bool is_table_fd_item() const
  {
    return false;
  }
  virtual inline bool is_expr_fd_item() const
  {
    return false;
  }

  virtual int assign(const ObFdItem& other);
  virtual int check_expr_in_child(const ObRawExpr* expr, const EqualSets& equal_sets, bool& is_in_child) const;
  int check_exprs_in_child(const ObIArray<ObRawExpr*>& exprs, const EqualSets& equal_sets, bool& is_in_child) const;
  int check_exprs_in_child(
      const ObIArray<ObRawExpr*>& exprs, const EqualSets& equal_sets, const int64_t pos_start, ObSqlBitSet<> exprs_set);
  int check_exprs_in_child(const common::ObIArray<OrderItem>& order_items, const EqualSets& equal_sets,
      const int64_t pos_start, ObSqlBitSet<> exprs_set);

  VIRTUAL_TO_STRING_KV(K_(parent_exprs), K_(is_unique), K_(stmt_level));

protected:
  ObRawExprSet* parent_exprs_;
  bool is_unique_;
  int32_t stmt_level_;
};

typedef common::ObSEArray<ObFdItem*, 8, common::ModulePageAllocator, true> ObFdItemSet;

class ObTableFdItem : public ObFdItem {
public:
  ObTableFdItem() : ObFdItem(), child_tables_()
  {}
  ObTableFdItem(const bool is_unique, ObRawExprSet* parent_exprs, const int32_t stmt_level)
      : ObFdItem(is_unique, parent_exprs, stmt_level), child_tables_()
  {}
  virtual ~ObTableFdItem()
  {}

  virtual int assign(const ObTableFdItem& other);
  virtual int check_expr_in_child(const ObRawExpr* expr, const EqualSets& equal_sets, bool& is_in_child) const override;

  inline ObRelIds& get_child_tables()
  {
    return child_tables_;
  }
  inline const ObRelIds& get_child_tables() const
  {
    return child_tables_;
  }
  virtual FdLevel get_level() const override;
  virtual bool is_table_fd_item() const override;

  VIRTUAL_TO_STRING_KV(K_(parent_exprs), K_(is_unique), K_(stmt_level), K_(child_tables));

protected:
  ObRelIds child_tables_;
};
inline FdLevel ObTableFdItem::get_level() const
{
  return TABLE_LEVEL;
}
inline bool ObTableFdItem::is_table_fd_item() const
{
  return true;
}

class ObExprFdItem : public ObFdItem {
public:
  ObExprFdItem() : ObFdItem(), inner_alloc_("ExprFdItem"), child_exprs_(&inner_alloc_)
  {}
  ObExprFdItem(const bool is_unique, ObRawExprSet* parent_exprs, const int32_t stmt_level)
      : ObFdItem(is_unique, parent_exprs, stmt_level), inner_alloc_("ExprFdItem"), child_exprs_(&inner_alloc_)
  {}
  virtual ~ObExprFdItem()
  {}

  virtual int assign(const ObExprFdItem& other);
  virtual int check_expr_in_child(const ObRawExpr* expr, const EqualSets& equal_sets, bool& is_in_child) const override;

  ObRawExprSet& get_child_exprs()
  {
    return child_exprs_;
  }
  const ObRawExprSet& get_child_exprs() const
  {
    return child_exprs_;
  }
  virtual FdLevel get_level() const override;
  virtual bool is_expr_fd_item() const override;

  VIRTUAL_TO_STRING_KV(K_(parent_exprs), K_(is_unique), K_(stmt_level), K_(child_exprs));

protected:
  common::ModulePageAllocator inner_alloc_;
  ObRawExprSet child_exprs_;
};
inline FdLevel ObExprFdItem::get_level() const
{
  return EXPR_LEVEL;
}
inline bool ObExprFdItem::is_expr_fd_item() const
{
  return true;
}

class ObFdItemFactory {
public:
  explicit ObFdItemFactory(common::ObIAllocator& alloc) : allocator_(alloc), item_store_(alloc), item_set_store_(alloc)
  {}
  ~ObFdItemFactory()
  {}

  int create_fd_item_set(ObFdItemSet*& fd_item_set);

  int get_parent_exprs_ptr(const ObIArray<ObRawExpr*>& parent_exprs, ObRawExprSet*& parent_exprs_ptr);
  int get_parent_exprs_ptr(ObRawExpr* parent_expr, ObRawExprSet*& parent_exprs_ptr);

  int create_table_fd_item(
      ObTableFdItem*& fd_item, const bool is_unique, ObRawExprSet* parent_exprs, const int32_t stmt_level);

  int create_table_fd_item(ObTableFdItem*& fd_item, const bool is_unique, const ObIArray<ObRawExpr*>& parent_exprs,
      const int32_t stmt_level, const ObRelIds& table_set);

  int create_table_fd_item(ObTableFdItem*& fd_item, const ObTableFdItem& other);

  int create_expr_fd_item(
      ObExprFdItem*& fd_item, const bool is_unique, ObRawExprSet* parent_exprs, const int32_t stmt_level);

  int create_expr_fd_item(ObExprFdItem*& fd_item, const bool is_unique, const ObIArray<ObRawExpr*>& parent_exprs,
      const int32_t stmt_level, const ObIArray<ObRawExpr*>& child_exprs);

  int create_expr_fd_item(ObExprFdItem*& fd_item, const ObExprFdItem& other);

  int copy_fd_item(ObFdItem*& fd_item, const ObFdItem& other);

  int deduce_fd_item_set(const EqualSets& equal_sets, ObIArray<ObRawExpr*>& column_exprs,
      ObIArray<ObRawExpr*>& const_exprs, ObFdItemSet& fd_item_set);

  int do_deduce_fd_item_set(const EqualSets& equal_sets, ObIArray<ObRawExpr*>& column_exprs,
      ObIArray<ObRawExpr*>& const_exprs, ObFdItemSet& fd_item_set, const bool final_deduce, ObSqlBitSet<>& deduced_fd,
      ObIArray<ObFdItem*>& new_fd_items);

  inline common::ObIAllocator& get_allocator()
  {
    return allocator_;
  }
  TO_STRING_KV("", "");

private:
  common::ObIAllocator& allocator_;
  common::ObObjStore<ObFdItem*, common::ObIAllocator&, true> item_store_;
  common::ObObjStore<ObFdItemSet*, common::ObIAllocator&, true> item_set_store_;
  ObRawExprSets parent_sets_;

private:
  DISALLOW_COPY_AND_ASSIGN(ObFdItemFactory);
};

}  // end of namespace sql
}  // end of namespace oceanbase

#endif  // OCEANBASE_SQL_OB_FD_ITEM_H
